package com.hzhr.iot.core.util.generator;

import com.hzhr.iot.core.util.HzhrFileUtil;
import com.hzhr.iot.core.util.HzhrReflectUtil;
import com.hzhr.iot.core.util.HzhrStringUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

public class SdkGenerator {
    public static String projectName;
    public static String serviceName;
    public static String shortName;
    public static String rootPath;
    public static String sdkRootPath;
    public static String rootPackageName;

    public static void syncModel(Class[] ignoredModelClass) throws Exception {
        //删除dto
        HzhrFileUtil.deleteDirectory(sdkRootPath + "\\model");
        //复制dto
        String srcRootPath = rootPath;
        copyDirectiory(srcRootPath + "\\model", sdkRootPath + "\\model");
        //删除多余的dto
        for (int i = 0; i < ignoredModelClass.length; i++) {
            Class clazz = ignoredModelClass[i];
            String className = clazz.getName();
            className = className.replace(rootPackageName, "");
            String fileName = sdkRootPath + className.replace(".", "\\") + ".java";
            HzhrFileUtil.deleteFile(fileName);
        }
    }
    public static void updateSdkModel() throws Exception {
        List<File> files = HzhrFileUtil.getFiles(sdkRootPath + "\\model");
        for (File file : files) {
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStreamReader reader = new InputStreamReader(fileInputStream);
            BufferedReader bufferedReader = new BufferedReader(reader);
            String content = "";
            String line = "";
            boolean flag = false;
            int continueCount = 0;
            List<String> removeProps = new ArrayList<>();
            while ((line = bufferedReader.readLine()) != null) {
                if (line.contains("@CygModel") || (line.contains(".entity.") && line.contains("import"))) continue;
                if (line.contains(".sdk.") && !line.contains("." + shortName + ".sdk.")) continue;
                if (line.startsWith("import " + rootPackageName) && !line.startsWith("import " + rootPackageName + ".sdk")) {
                    line = line.replace("import " + rootPackageName, "import " + rootPackageName + ".sdk");
                }
                if (line.startsWith("package " + rootPackageName)) {
                    line = line.replace(rootPackageName, rootPackageName + ".sdk");
                }

                content += line + "\n";
            }
            HzhrFileUtil.writeContent(file.getPath(), content);
        }
    }
    public static void syncConstant() throws Exception {
        //删除dto
        HzhrFileUtil.deleteDirectory(sdkRootPath + "\\constant");
        //复制dto
        String srcRootPath = rootPath;
        File file = new File(sdkRootPath + "\\constant");
        if(!file.exists()){
            file.mkdirs();
        }
        HzhrFileUtil.copyFile(new File(srcRootPath + "\\constant\\" + HzhrStringUtils.firstCharToUppserCase(shortName) + "_ErrorsEnum.java"), new File(sdkRootPath + "\\constant\\" + HzhrStringUtils.firstCharToUppserCase(shortName) + "_ErrorsEnum.java"));
    }
    public static void updateSdkConstant() throws Exception {
        List<File> files = HzhrFileUtil.getFiles(sdkRootPath + "\\constant");
        for (File file : files) {
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStreamReader reader = new InputStreamReader(fileInputStream);
            BufferedReader bufferedReader = new BufferedReader(reader);
            String content = "";
            String line = "";
            boolean flag = false;
            int continueCount = 0;
            List<String> removeProps = new ArrayList<>();
            while ((line = bufferedReader.readLine()) != null) {
                if (line.startsWith("import " + rootPackageName) && !line.startsWith("import " + rootPackageName + ".sdk")) {
                    line = line.replace("import " + rootPackageName, "import " + rootPackageName + ".sdk");
                }
                if (line.startsWith("package " + rootPackageName)) {
                    line = line.replace(rootPackageName, rootPackageName + ".sdk");
                }

                content += line + "\n";
            }
            HzhrFileUtil.writeContent(file.getPath(), content);
        }
    }
    public static void generateService() throws Exception {
        String srcRootPath = rootPath;
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();

        List<File> files1 = HzhrFileUtil.getFiles(srcRootPath + "\\controller");
        for (File file1:
                files1) {
            String fileName = file1.getName();
            if (!fileName.equals("BaseController.java") && fileName.endsWith("Controller.java")) {
                String controllerName = fileName.substring(0, fileName.lastIndexOf(".java"));
                String resourceName = fileName.substring(0, fileName.lastIndexOf("Controller.java"));
                String resourceNameFl = HzhrStringUtils.firstCharToLowerCase1(resourceName);
                Class<?> controllerClass = Class.forName(rootPackageName + ".controller." + controllerName);

                String serviceName1 = resourceName + "Service";
                File serviceFile = null;
                try {
                    serviceFile = new File(rootPath + "\\service\\" + serviceName1 + ".java");
                } catch (Exception e) {

                }
                Map<String, List<String>> shuomingMap = null;
                if (serviceFile != null) {
                    shuomingMap = new HashMap<>();
                    FileInputStream fis = new FileInputStream(serviceFile);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    br.mark((int)serviceFile.length() + 1);

                    String line = null;
                    boolean annoFlag = false;
                    String functionName = "";
                    while ((line = br.readLine()) != null) {
                        if (annoFlag) {
                            if (!line.contains("//endregion")) {
                                List<String> shuoming = shuomingMap.get(functionName);
                                if (shuoming == null) {
                                    shuoming = new ArrayList<>();
                                }
                                shuoming.add(line);
                                shuomingMap.put(functionName, shuoming);
                            } else {
                                annoFlag = false;
                            }
                        }

                        if (line.contains("//region javadoc注释 : ")) {
                            functionName = line.substring(line.indexOf(" : ") + 3, line.length());
                            annoFlag = true;
                        }
                    }
                }

                StringBuilder importSb = new StringBuilder();
                StringBuilder contentSb = new StringBuilder();

                importSb.append("package " + rootPackageName + ".sdk.service;\n\n");
                importSb.append("import com.hzhr.iot.core.constant.Common_ErrorsEnum;\n");
                importSb.append("import com.hzhr.iot.core.dto.ResponseExceptionDto;\n");
                importSb.append("import com.hzhr.iot.core.exception.ServiceException;\n");
                importSb.append("import com.hzhr.iot.core.util.HzhrHttpUtil;\n");
                importSb.append("import com.hzhr.iot.core.util.HzhrJsonUtil;\n");
                importSb.append("import " + rootPackageName + ".sdk.constant." + HzhrStringUtils.firstCharToUppserCase(shortName) + "_ErrorsEnum;\n");
                Set<Class<?>> classes = HzhrReflectUtil.getClasses(rootPackageName + ".model." + resourceNameFl);
                for (Class<?> clazz:
                classes) {
                    String clazzName = clazz.getName();
                    if (!clazzName.endsWith("Model") && !clazzName.endsWith("Response") && !clazzName.endsWith("Dto")) {
                        continue;
                    }
                    clazzName = clazzName.replaceFirst(".model.", ".sdk.model.");
                    importSb.append("import " +  clazzName + ";\n");
                }
                importSb.append("import org.springframework.beans.factory.annotation.Autowired;\n");
                importSb.append("import org.springframework.http.HttpStatus;\n");
                importSb.append("import org.springframework.http.HttpEntity;\n");
                importSb.append("import org.springframework.http.HttpHeaders;\n");
                importSb.append("import org.springframework.http.HttpMethod;\n");
                importSb.append("import org.springframework.http.ResponseEntity;\n");
                importSb.append("import org.springframework.stereotype.Service;\n");
                importSb.append("import org.springframework.web.client.HttpClientErrorException;\n");
                importSb.append("import org.springframework.web.client.HttpServerErrorException;\n");
                importSb.append("import org.springframework.web.client.RestTemplate;\n");
                importSb.append("import org.springframework.web.client.RestTemplate;\n");
                importSb.append("import java.util.HashMap;\n");
                importSb.append("import java.util.Map;\n");
                importSb.append("\n");

                contentSb.append("@Service\n");
                contentSb.append("public class " + HzhrStringUtils.firstCharToUppserCase(shortName) + "_" + resourceName + "Service {\n");
                contentSb.append("    @Autowired\n");
                contentSb.append("    RestTemplate restTemplate;\n");
                contentSb.append("\n");

                Method[] methods = controllerClass.getDeclaredMethods();
                for (Method method:
                     methods) {
                    String methodName = method.getName();
                    String[] parameterNames = u.getParameterNames(method);
                    Class[] parameterTypes = method.getParameterTypes();
                    Type[] ts = ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments();
                    Class<?> returnType = (Class<?>)ts[0];
                    String uri = method.getAnnotation(RequestMapping.class).value()[0];
                    String httpMethod = method.getAnnotation(RequestMapping.class).method()[0].name();

                    boolean hasBody = false;
                    if (shuomingMap != null) {
                        List<String> shuomings = shuomingMap.get(methodName);
                        for (String shuoming :
                        shuomings) {
                            contentSb.append(shuoming + "\n");
                        }
                        contentSb.substring(0, contentSb.length() - 1);
                    }
                    contentSb.append("    public " + returnType.getSimpleName() + " " + methodName + "(");
                    int i = 0;
                    contentSb.append("Map<String, String> header, ");
                    for (String parameterName :
                    parameterNames) {
                        if (parameterName.equals("query")) {
                            contentSb.append("Map<String, Object> query");
                        } else if (parameterName.equals("body")) {
                            hasBody = true;
                            contentSb.append(parameterTypes[i].getSimpleName() + " " + parameterName);
                        } else {
                            contentSb.append(parameterTypes[i].getSimpleName() + " " + parameterName);
                        }

                        if (i < parameterNames.length - 1) {
                            contentSb.append(", ");
                        }
                        i++;
                    }
                    contentSb.append(") throws ServiceException {\n");

                    contentSb.append("        HttpHeaders headers = new HttpHeaders();\n");
                    contentSb.append("        if (header == null) header = new HashMap<>();\n");
                    contentSb.append("        for (Map.Entry<String, String> entry:\n");
                    contentSb.append("        header.entrySet()) {\n");
                    contentSb.append("            headers.add(entry.getKey(), entry.getValue());\n");
                    contentSb.append("        }\n");
                    contentSb.append("        headers.add(\"Content-Type\", \"application/json;charset-utf-8\");\n");
                    contentSb.append("        headers.add(\"accept\", \"application/json\");\n");
                    if (hasBody) {
                        contentSb.append("        HttpEntity httpEntity = new HttpEntity<>(body, headers);\n");
                    } else {
                        contentSb.append("        HttpEntity httpEntity = new HttpEntity<>(null, headers);\n");
                    }
                    contentSb.append("\n");

                    contentSb.append("        Map<String, Object> uriVariables = new HashMap<>();\n");
                    for (String parameterName :
                    parameterNames) {
                        if (!parameterName.equals("header") && !parameterName.equals("query") && !parameterName.equals("body")) {
                            contentSb.append("        uriVariables.put(\"" + parameterName + "\", " + parameterName + ");\n");
                        }
                    }
                    contentSb.append("\n");

                    contentSb.append("        try {\n");
                    contentSb.append("            ResponseEntity<" + returnType.getSimpleName() + "> result = restTemplate.exchange(\"http://" + serviceName + uri + "?\" + HzhrHttpUtil.queryMap2url(query), HttpMethod." + httpMethod + ", httpEntity, " + returnType.getSimpleName() + ".class, uriVariables);\n");
                    contentSb.append("            return result.getBody();\n");
                    contentSb.append("        } catch (HttpClientErrorException e) {\n");
                    contentSb.append("            e.printStackTrace();\n");
                    contentSb.append("            if (e.getStatusCode().equals(HttpStatus.UNAUTHORIZED)) {\n");
                    contentSb.append("                throw new ServiceException(Common_ErrorsEnum.getEnumByName(\"COMMON_2\"));\n");
                    contentSb.append("            }\n");
                    contentSb.append("            ResponseExceptionDto response = HzhrJsonUtil.json2object(e.getResponseBodyAsString(), ResponseExceptionDto.class);\n");
                    contentSb.append("            Integer code = response.getCode();\n");
                    contentSb.append("            if (code <= 100) {\n");
                    contentSb.append("                throw new ServiceException(Common_ErrorsEnum.getEnumByName(\"COMMON_\" + code));\n");
                    contentSb.append("            } else {\n");
                    contentSb.append("                 throw new ServiceException(" + HzhrStringUtils.firstCharToUppserCase(shortName) + "_ErrorsEnum.getEnumByName(\"" + resourceName.toUpperCase() + "SERVICE_" + HzhrStringUtils.splitHumpStr(methodName, "_").toUpperCase() + "_\" + code));\n");
                    contentSb.append("            }\n");
                    contentSb.append("        } catch (HttpServerErrorException e) {\n");
                    contentSb.append("            e.printStackTrace();\n");
                    contentSb.append("            ResponseExceptionDto response = HzhrJsonUtil.json2object(e.getResponseBodyAsString(), ResponseExceptionDto.class);\n");
                    contentSb.append("            Integer code = response.getCode();\n");
                    contentSb.append("            if (code <= 100) {\n");
                    contentSb.append("                throw new ServiceException(Common_ErrorsEnum.getEnumByName(\"COMMON_\" + code));\n");
                    contentSb.append("             } else {\n");
                    contentSb.append("                 throw new ServiceException(" + HzhrStringUtils.firstCharToUppserCase(shortName) + "_ErrorsEnum.getEnumByName(\"" + resourceName.toUpperCase() + "SERVICE_" + HzhrStringUtils.splitHumpStr(methodName, "_").toUpperCase() + "_\" + code));\n");
                    contentSb.append("             }\n");
                    contentSb.append("        } catch (Throwable e) {\n");
                    contentSb.append("              e.printStackTrace();\n");
                    contentSb.append("              throw e;\n");
                    contentSb.append("        }\n");
                    contentSb.append("    }\n");
                }

                contentSb.append("}\n");

                HzhrFileUtil.writeContent(sdkRootPath + "\\service\\" + HzhrStringUtils.firstCharToUppserCase(shortName) + "_" + resourceName + "Service.java", importSb.toString() + contentSb.toString());
            }
        }
    }

    // 复制文件夹
    public static void copyDirectiory(String sourceDir, String targetDir)
            throws IOException {
        // 新建目标目录
        (new File(targetDir)).mkdirs();
        // 获取源文件夹当前下的文件或目录
        File[] file = (new File(sourceDir)).listFiles();
        for (int i = 0; i < file.length; i++) {
            if (file[i].isFile()) {
                // 源文件
                File sourceFile=file[i];
                String fileName = sourceFile.getName();
                if (!fileName.endsWith("_set.java") && !fileName.endsWith("_add.java")) {
                    // 目标文件
                    File targetFile=new
                            File(new File(targetDir).getAbsolutePath()
                            +File.separator+file[i].getName());
                    HzhrFileUtil.copyFile(sourceFile,targetFile);
                }
            }
            if (file[i].isDirectory()) {
                // 准备复制的源文件夹
                String dir1=sourceDir + "/" + file[i].getName();
                // 准备复制的目标文件夹
                String dir2=targetDir + "/"+ file[i].getName();
                copyDirectiory(dir1, dir2);
            }
        }
    }
}
